﻿using System;
using System.Collections.Generic;
using System.Text;
using Xeml.Document.Contracts;
using Xeml.Document.EnvironmentalOntology;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace Xeml.Document.Ontologies.Positioning
{
    public class PosTerm : IVariableOntologyTerm
    {
        private string _name;
        //private int _recomLevel;
        private string _groupPath;
        private string _defintion;
        private string _nameSpaceAlias;
        private string _nameSpace;
        private Dictionary<string, VariableContextSpec> _contextCollection;
        private string _termId;
        private List<Term> _supervisedObjects;
        private ReadOnlyCollection<Term> _readOnlySupervisedObjects;
        private TermAttributeCollection _attributes = new TermAttributeCollection();

        internal string NameSpace
        {
            get { return _nameSpace; }
            set { _nameSpace = value; }
        }
        internal string NameSpaceAlias
        {
            get { return _nameSpaceAlias; }
        }
        internal Dictionary<string, VariableContextSpec> ContextCollection
        {
            get { return _contextCollection; }
            set { _contextCollection = value; }
        }

        internal string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }

        internal int RecommendationLevel
        {
            get
            {
                if (_attributes.Contains("RecommendationLevel"))
                    return Int16.Parse(_attributes.Find("RecommendationLevel").Value);
                else
                    return default(int);
            }
            set
            {
                if (_attributes.Contains("RecommendationLevel"))
                {
                    _attributes.Add(new TermAttribute("RecommendationLevel", value.ToString()));
                }
                else
                {
                    _attributes.Remove("RecommendationLevel");
                    _attributes.Add(new TermAttribute("RecommendationLevel", value.ToString()));
                }
            }
        }

        internal string GroupPath
        {
            get
            {
                return _groupPath;
            }
            set
            {
                _groupPath = value;
            }
        }

        internal string[] GroupLevels
        {
            get
            {
                return _groupPath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            }
        }

        #region IVariableOntologyTerm Members

        public string[] Contexts
        {
            get
            {
                string[] ret = new string[ContextCollection.Count];
                ContextCollection.Keys.CopyTo(ret, 0);
                return ret;
            }
        }

        public int ContextCount
        {
            get
            {
                return _contextCollection.Count;
            }
        }

        public bool HasDefaultValue(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                return this.ContextCollection[context].DefaultValue != null;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        public bool HasDefaultUnit(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                VariableContextSpec pcs = this.ContextCollection[context];
                return pcs.UnitSet.DefaultUnit != null;
            }

            else
                throw new ArgumentException("Unknown context", context);
        }

        public string DefaultUnitName(string context)
        {
            if (HasDefaultUnit(context))
            {
                VariableContextSpec pcs = this.ContextCollection[context];
                return pcs.UnitSet.DefaultUnit.Name;
            }
            else
                return null;
        }

        public string DefaultUnitSymbol(string context)
        {
            if (HasDefaultUnit(context))
            {
                VariableContextSpec pcs = this.ContextCollection[context];
                return pcs.UnitSet.DefaultUnit.Symbol;
            }
            else
                return null;
        }

        public List<KeyValuePair<string, string>> ListUnits(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                List<KeyValuePair<string, string>> ret = new List<KeyValuePair<string, string>>();
                VariableContextSpec pcs = this.ContextCollection[context];
                if (pcs != null)
                {
                    if (pcs.UnitSet.DefaultUnit != null)
                        ret.Add(new KeyValuePair<string, string>(pcs.UnitSet.DefaultUnit.Name, pcs.UnitSet.DefaultUnit.Symbol));
                    foreach (ConvertableUnit cu in pcs.UnitSet.ConvertableUnits)
                    {
                        ret.Add(new KeyValuePair<string, string>(cu.Name, cu.Symbol));
                    }
                }
                return ret;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        public VariableDataTypes GetDataType(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                return this.ContextCollection[context].TypeDefine.BaseType;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        public decimal? GetMin(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                Min m = this.ContextCollection[context].TypeDefine.GetMin();
                if (m != null)
                    return m.NumberVal;
                else
                    return null;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        public decimal? GetMax(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                Max m = this.ContextCollection[context].TypeDefine.GetMax();
                if (m != null)
                    return m.NumberVal;
                else
                    return null;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        public List<KeyValuePair<string, string>> GetEnums(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                List<KeyValuePair<string, string>> ret = new List<KeyValuePair<string, string>>();
                VariableContextSpec pcs = this.ContextCollection[context];
                foreach (Xeml.Document.EnvironmentalOntology.Enum e in pcs.TypeDefine.ListEnums())
                {
                    ret.Add(new KeyValuePair<string, string>(e.TextVal, e.TextVal));
                }
                return ret;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        public ValueBase DefaultValue(string context)
        {
            if (this.ContextCollection.ContainsKey(context))
            {
                VariableContextSpec pcs = this.ContextCollection[context];
                Value v = new Value();
                v.Context = context;
                if (HasDefaultValue(context))
                {
                    v.Val = pcs.DefaultValue;
                }
                else
                {
                    switch (pcs.TypeDefine.BaseType)
                    {
                        case VariableDataTypes.Number:
                            {
                                v.DoubleValue = default(double);
                                break;
                            }
                        case VariableDataTypes.Text:
                            {
                                v.Val = "";
                                break;
                            }
                        case VariableDataTypes.Bool:
                            {
                                v.BooleanValue = default(bool);
                                break;
                            }
                        default:
                            {
                                v.Val = "";
                                break;
                            }
                    }
                }
                if (HasDefaultUnit(context))
                    v.Unit = pcs.UnitSet.DefaultUnit.Symbol;
                return v;
            }
            else
                throw new ArgumentException("Unknown context", context);
        }

        #endregion

        #region IOntologyTerm Members

        public string TermId
        {
            get
            {
                return _termId;
            }
        }

        public IEnumerable<Term> SupervisedObjects
        {
            get { return _readOnlySupervisedObjects; }
        }

        public Term Prototype
        {
            get { return EmitDefaultObject(false); }
        }

        public string Definition
        {
            get { return _defintion; }
            set { _defintion = value; }
        }

        public TermAttributeCollection Attributes
        {
            get
            {
                return _attributes;
            }
        }

        public Term EmitDefaultObject(bool supervised)
        {
            VariableTerm p = new VariableTerm(_termId);
            p.Name = this.Name;
            // p.GroupPath = this.GroupPath;
            p.NameSpaceAlias = this.NameSpaceAlias;
            p.NameSpace = this.NameSpace;
            foreach (VariableContextSpec pcs in ContextCollection.Values)
            {
                p.Values.Add((Value)this.DefaultValue(pcs.Name));
            }
            if (supervised)
                Supervise(p);
            return p;
        }

        public void Supervise(Term t)
        {
            if (t.TermId == this.TermId)
            {
                if (!_supervisedObjects.Contains(t))
                {
                    _supervisedObjects.Add(t);
                    t.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
                    ValidateSupervisedProperties();
                }
            }
            else
                throw new ArgumentException("TermId missmatch. Unable to supervice term", "Term");
        }

        public void RejectSupervison(Term t)
        {
            if (_supervisedObjects.Contains(t))
            {
                _supervisedObjects.Remove(t);
                t.PropertyChanged -= new PropertyChangedEventHandler(OnPropertyChanged);
            }
        }

        public void ValidateSupervisedProperties()
        {
            ValidateProperty("Name");
            ValidateProperty("Values");
        }

        private void ValidateProperty(string p)
        {
            foreach (Term param in this._supervisedObjects)
            {


                switch (p)
                {
                    case "Name":
                        {
                            if (param.Name != _name)
                                OnValidation(SeverityCode.Error, "The parameter name differs from the ontology term", param);
                            break;
                        }

                    case "Values":
                        {
                            //TODO
                            OnValidation(SeverityCode.Warning, "The value validation is not implemented yet.", param);

                            break;
                        }
                    default:
                        break;
                }
            }
        }

        public void PushSupervisedProperties()
        {
            foreach (Term param in _supervisedObjects)
            {
                param.Name = this.Name;
            }
        }


        void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            ValidateProperty(e.PropertyName);
        }

        public event EventHandler<ValidationMessage> Validation;
        private void OnValidation(SeverityCode sc, string message, Term p)
        {
            if (Validation != null)
            {
                ValidationMessage vm = new ValidationMessage(sc, message, p, false);
                Validation(this, vm);
            }
        }
        #endregion

        public PosTerm(string termId, string nameSpace, string nameSpaceAlias)
        {
            _supervisedObjects = new List<Term>();
            _readOnlySupervisedObjects = new ReadOnlyCollection<Term>(_supervisedObjects);
            _termId = termId;
            _nameSpace = nameSpace;
            _nameSpaceAlias = nameSpaceAlias;
            _contextCollection = new Dictionary<string, VariableContextSpec>();
        }
    }
}
